package cn.bigcontainer.alt.meta.aspect;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestParam;

import cn.bigcontainer.alt.common.exception.BigException;
import cn.bigcontainer.alt.common.exception.BigResultCode;
import cn.bigcontainer.alt.common.util.WoKit;
import cn.bigcontainer.alt.meta.dto.MAppDto;
import lombok.extern.slf4j.Slf4j;

/**
 * Controller参数预处理，并做统一异常控制
 */
@Component
@Order(99)
@Aspect
@Slf4j
public class ControllerAspect {

    @Resource
    private HttpServletRequest request;

    @Around("execution(* cn.bigcontainer.alt..*Controller.*(..))")
    @Transactional
    public Object proceed(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        log.info(String.format("Process controller method %s, return type %s.", signature.getName(), signature.getReturnType().getName()));
        Class<?> targetClass = joinPoint.getTarget().getClass();
        Method targetClassMethod = null;
        try {
            targetClassMethod = targetClass.getMethod(signature.getName(), signature.getParameterTypes());
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(String.format("ProceedingJoinPoint method '%s' is not found.", signature.getName()), e);
        }
        Object[] args = joinPoint.getArgs();
        Annotation[][] parameterAnnotations = targetClassMethod.getParameterAnnotations();
        // @RequestParam参数的索引，-1表示没有
        int requestMapIndex = -1;
        for (int i = 0; i < parameterAnnotations.length; i ++) {
            Annotation[] parameterAnnotation = parameterAnnotations[i];
            for (Annotation a : parameterAnnotation) {
                if (a instanceof RequestParam && args[i] instanceof Map) {
                    requestMapIndex = i;
                }
            }
        }
        // 查找第一个不是requestMapIndex的Map，其中存储了session数据
        Map<String, Object> map = null;
        for (int i = 0; i < args.length; i ++) {
            if (map == null && args[i] instanceof Map && i != requestMapIndex) {
                map = (Map<String, Object>) args[i];
            }
        }
        if (requestMapIndex >= 0) {
            // 获取request数据
            Map<String, Object> req = (Map<String, Object>)args[requestMapIndex];
            // parseFile(req, map);
            args[requestMapIndex] = req;
            // 将request数据中key为MAppDto.PARAM_REQUEST_JSON的值当做json（HashMap）解析，并放入request数据中
            Object reqjson = req.get(MAppDto.PARAM_REQUEST_JSON);
            if (reqjson != null) {
                WoKit.putAll(req, reqjson.toString());
            }
            // 在request数据中增加session数据
            if (map != null) {
            	req.putAll(map);
            }
            try {
            	Object result = joinPoint.proceed(args);
            	// log.info("org.aspectj.lang.ProceedingJoinPoint.proceed(Object[]) return:" + result.getClass().getName());
                return result;
            } catch (Throwable throwable) {
            	return handleException(throwable, signature.getReturnType(), map);
            }
        }
        try {
        	Object result = joinPoint.proceed();
        	// log.info("org.aspectj.lang.ProceedingJoinPoint.proceed() return:" + result.getClass().getName());
            return result;
        } catch (Throwable throwable) {
            return handleException(throwable, signature.getReturnType(), map);
        }
    }

    protected String getErrorPage () {
        return "error";
    }

    /**
     * 统一异常处理
     */
    protected Object handleException(Throwable throwable, Class<?> returnType, Map<String, Object> map) {
    	log.error("统一异常处理", throwable);
    	if (returnType.equals(BigResultCode.class)) {
    		if (throwable instanceof BigException) {
    			return ((BigException)throwable).getCode();
    		}
    		return BigResultCode.getUnknownCode().setMsg("未知错误：" + throwable.getMessage());
    	}
    	if (map != null) {
    		map.put("error", throwable.getMessage());
    	}
    	return getErrorPage ();
    }
}
